/******************************************************************************
 *   ObjectFS - Embedded Object File-System
 *
 *   Provides a full, simple no-nonsense file-system for simple embedded devices.
 *   Written by James Walmsley, james@fullfat-fs.co.uk
 *
 *   Copyright 2010 James Walmsley
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 *
 *
 *   Removing the above notice is illegal and will invalidate this license.
 *****************************************************************************/

/*
	OFS Object Manager
*/

#ifdef __PIC24FJ64GB106__

#include <string.h>
#include "ofs.h"
#include "ofs_config.h"
#include "ofs_types.h"
#include "ofs_error.h"
#include "ofs_memory.h"
#include <p24fxxxx.h>

OFS_ERROR OFS_InitObjectManager(OFS_MANAGER *pOman, OFS_DEV_READ pfnRead, OFS_DEV_WRITE pfnWrite, OFS_DEV_ERASE pfnErase, void *pParam) {
	memset(pOman, 0, sizeof(OFS_MANAGER));

	pOman->pfnReadDevice = pfnRead;
	pOman->pfnWriteDevice = pfnWrite;
	pOman->pfnEraseDevice = pfnErase;
	pOman->pParam = pParam;

	return OFS_ERR_NONE;
}

OFS_ERROR OFS_FormatFlash(OFS_MANAGER *pOman, OFS_UINT32 nDescriptors, const OFS_INT8 *szpVolumeName) {
	OFS_UINT8 DescriptorHeader[32];

	if(pOman->pfnEraseDevice) {
		pOman->pfnEraseDevice(pOman->pParam);
	}

	memset(DescriptorHeader, 0, 32);						// Erase descriptor table header.

	OFS_putLong(DescriptorHeader, 0x00, 0x0F5A0F5A);		// Write magic number.
	OFS_putLong(DescriptorHeader, 0x04, nDescriptors);		// Write number of descriptors field.

	strncpy((OFS_INT8 *) &DescriptorHeader[0x10], szpVolumeName, 16);	// Copy volume name, terminating only if less than 16 chars.

	OFS_WriteDevice(pOman, 0, 32, DescriptorHeader);		// Write header to flash.

	return OFS_ERR_NONE;
}

OFS_ERROR OFS_MountFilesystem(OFS_MANAGER *pOMan) {
	OFS_UINT8 DescriptorHeader[32];

	OFS_ReadDevice(pOMan, 0, 32, DescriptorHeader);

	if(OFS_getLong(DescriptorHeader, 0) != 0x0F5A0F5A) {
		return OFS_ERR_INVALID_IDENTIFIER;
	}

	pOMan->ulTotalDescriptors = OFS_getLong(DescriptorHeader, 0x04);
	pOMan->ulDataBeginAddress = ((pOMan->ulTotalDescriptors * 32) + 32);

	return OFS_ERR_NONE;
}

static OFS_BOOL objectExists(OFS_MANAGER *pOMan, const OFS_INT8 *szpName, OFS_UINT8 *Dirent) {
	OFS_INT i;
	OFS_TYPE type;
	for(i = 0; i < pOMan->ulTotalDescriptors; i++) {
		OFS_ReadDevice(pOMan, ((i * 32) + 32), 32, Dirent);
		type = OFS_getLong(Dirent, 0);
		if(type != 0xFFFFFFFF && type != 0x00000000) {	// Un modified entry -- slot is free.
			if(!strncmp(szpName, (OFS_INT8*) &Dirent[16], 16)) {
				return OFS_TRUE;
			}
		}
	}
	return OFS_FALSE;
}

static OFS_UINT32 getFreeDirent(OFS_MANAGER *pOMan, OFS_UINT8 *Dirent) {
	OFS_INT i;

	for(i = 0; i < pOMan->ulTotalDescriptors; i++) {
		OFS_ReadDevice(pOMan, ((i * 32) + 32), 32, Dirent);
		if(OFS_getLong(Dirent, 0) == 0xFFFFFFFF) {	// Un modified entry -- slot is free.
			return (i*32) + 32;
		}
	}

	return 0;
}

static OFS_UINT32 getBeginFreespace(OFS_MANAGER *pOMan) {
	OFS_UINT8	Dirent[32];
	OFS_INT		i;
	OFS_UINT32	ulStartAddress = pOMan->ulDataBeginAddress;
	OFS_UINT32	ulSize = 0, ulStart = 0;

	ulStartAddress = (unsigned long)((pOMan->ulDataBeginAddress / (unsigned long)256) * (unsigned long)256);
	if(pOMan->ulDataBeginAddress % 256) {
		ulStartAddress += 256;
	}

	for(i = 0; i < pOMan->ulTotalDescriptors; i++) {
		OFS_ReadDevice(pOMan, ((i *32) + 32), 32, Dirent);
		if(OFS_getLong(Dirent, 0) != 0xFFFFFFFF) {	// Real entry detected.
			ulSize = OFS_getLong(Dirent, 0x08);
			ulStart = OFS_getLong(Dirent, 0x04);
		}
	}

	if(!ulStart) {
		ulStartAddress = ulStartAddress;
	} else {
		ulStartAddress = (ulStart + ((ulSize/256) * 256));
		if(ulSize % 256) {
			ulStartAddress += 256;
		}
	}

	/*ulStartAddress = (ulStartAddress) + ((ulTotalSize / (unsigned long)256) * (unsigned long)256);
	if(ulTotalSize % 256) {
		ulStartAddress += 256;
	}*/

	return ulStartAddress;
}

OFS_ERROR OFS_CreateObject(OFS_MANAGER *pOMan, const OFS_INT8 *szpName, OFS_UINT32 Size, OFS_TYPE eObjectType, OFS_TYPEDATA *oTypeData) {
	OFS_UINT8	Dirent[32];
	OFS_UINT32 freeDirentAddress = getFreeDirent(pOMan, Dirent);
	OFS_UINT32 ulStartAddress;

	if(objectExists(pOMan, szpName, Dirent)) {
		return -1;
	}

	if(!freeDirentAddress) {
		return -1;//OFS_ERR_OUT_OF_SPACE;
	}

	memset(Dirent, 0, 32);

	// Dirent now contains the dirent entry from flash.

	OFS_putLong(Dirent, 0x00, eObjectType);

	// Find the last available start address.
	ulStartAddress = getBeginFreespace(pOMan);

	OFS_putLong(Dirent, 0x04, ulStartAddress);
	OFS_putLong(Dirent, 0x08, Size);
	if(oTypeData) {
		OFS_putLong(Dirent, 0x0C, oTypeData->u32);
	}

	strncpy((OFS_INT8 *) &Dirent[0x10], szpName, 16);

	OFS_WriteDevice(pOMan, freeDirentAddress, 32, Dirent);

	return OFS_ERR_NONE;	// Successfully created a dirent.
}

OFS_ERROR OFS_ReadDevice(OFS_MANAGER *pOman, OFS_UINT32 ulAddress, OFS_UINT32 Size, void *pBuffer) {
	if(pOman->pfnReadDevice) {
		return pOman->pfnReadDevice(ulAddress, Size, pBuffer, pOman->pParam);
	}

	return OFS_ERR_NULL_POINTER;
}

OFS_ERROR OFS_WriteDevice(OFS_MANAGER *pOman, OFS_UINT32 ulAddress, OFS_UINT32 Size, void *pBuffer) {
	if(pOman->pfnWriteDevice) {
		if(ulAddress >= 0x900 && ulAddress <= 0x4550) {
			Nop();
		}
		return pOman->pfnWriteDevice(ulAddress, Size, pBuffer, pOman->pParam);
	}

	return OFS_ERR_NULL_POINTER;
}

#endif
